package com.jay.mypracticesdemo

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.Test
import java.util.concurrent.Executors
import kotlin.coroutines.CoroutineContext
import kotlin.time.Duration

class FlowTest {
    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun testLoadFlow(): Unit = runBlocking {
        class MyDispatcherProvider : CoroutineDispatcher() {
            override fun dispatch(context: CoroutineContext, block: Runnable) {
//                Executors.newCachedThreadPool().execute {
                    println("run block in MyDispatcherProvider as Main thread ${Thread.currentThread().name}")
                    block.run()
//                }
            }
        }
        Dispatchers.setMain(MyDispatcherProvider())
        val launch = MyIOConcurrentScope(Dispatchers.Main).launch {
            println("start runTest")
            val job = MyIOConcurrentScope().launch {
                runTest(timeout = Duration.INFINITE) {
                    val loadDataString = FlowDemo
                        .FakeDataLoadRepository()
                        .loadDataString(3)
                    val deferred = async {
                        val list = arrayListOf<String>()
                        loadDataString.collect {
                            println("collect $it")
//                            delay(15000)
                            list.add(it)
                        }
                        list
                    }
                    println("start toList")
                    println("deferred =${deferred.await()}")
                    val stringList = loadDataString
                        .map {
                            println("map $it")
                            delay(500)
                            println("map $it delay 500 ms")
                            it
                        }
                        .flowOn(Dispatchers.IO)
                        .toList()
                    println("end toList")
                    printList(stringList)
                    println("end forEach")
                }
            }
            println("end runTest")
            job.join()
            job.invokeOnCompletion {
                println("invokeOnCompletion2")
            }
        }
        launch.join()
        val disposableHandle = launch.invokeOnCompletion {
            println("invokeOnCompletion1")
        }
    }

    fun printList(list: List<String>) {
        list.forEach {
            println("forEach $it")
        }
    }
}